//! Test summary and reporting models
//! 
//! This module defines structures for test result aggregation and reporting,
//! corresponding to HttpRunner's Summary system.

use crate::models::{step::StepResult, Timestamp, now};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use uuid::Uuid;

/// Test summary for entire test execution (corresponds to HttpRunner's Summary)
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TestSummary {
    /// Overall success status
    pub success: bool,
    
    /// Test statistics
    pub stat: TestStat,
    
    /// Timing information
    pub time: TestTime,
    
    /// Platform information
    pub platform: Platform,
    
    /// Individual test case summaries
    pub details: Vec<TestCaseSummary>,
    
    /// Root directory for test execution
    pub root_dir: Option<String>,
}

/// Test case summary (corresponds to HttpRunner's TestCaseSummary)
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TestCaseSummary {
    /// Test case ID
    pub id: Uuid,
    
    /// Test case name
    pub name: String,
    
    /// Success status
    pub success: bool,
    
    /// Test statistics
    pub stat: TestCaseStat,
    
    /// Timing information
    pub time: TestTime,
    
    /// Input/output information
    pub in_out: InOutInfo,
    
    /// Test logs
    pub logs: Vec<LogEntry>,
    
    /// Step results
    pub step_results: Vec<StepResult>,
    
    /// Root directory
    pub root_dir: String,
}

/// Test statistics
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TestStat {
    /// Test case statistics
    pub test_cases: TestCaseStats,
    
    /// Test step statistics
    pub test_steps: TestStepStats,
}

/// Test case statistics
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TestCaseStats {
    /// Total test cases
    pub total: usize,
    
    /// Successful test cases
    pub success: usize,
    
    /// Failed test cases
    pub fail: usize,
}

/// Test step statistics
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TestStepStats {
    /// Total test steps
    pub total: usize,
    
    /// Successful test steps
    pub successes: usize,
    
    /// Failed test steps
    pub failures: usize,
    
    /// Action statistics by type
    pub actions: HashMap<String, usize>,
}

/// Individual test case statistics
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TestCaseStat {
    /// Total steps
    pub total: usize,
    
    /// Successful steps
    pub successes: usize,
    
    /// Failed steps
    pub failures: usize,
    
    /// Action statistics
    pub actions: HashMap<String, usize>,
}

/// Timing information
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TestTime {
    /// Start time
    pub start_at: Timestamp,
    
    /// Duration in seconds
    pub duration: f64,
}

/// Platform information
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Platform {
    /// Platform name
    pub platform_name: String,
    
    /// AI Test Platform version
    pub ai_test_platform_version: String,
    
    /// Rust version
    pub rust_version: String,
    
    /// Operating system
    pub platform: String,
}

/// Input/output information
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InOutInfo {
    /// Configuration variables
    pub config_vars: HashMap<String, serde_json::Value>,
    
    /// Exported variables
    pub export_vars: HashMap<String, serde_json::Value>,
}

/// Log entry
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LogEntry {
    /// Log level
    pub level: LogLevel,
    
    /// Log message
    pub message: String,
    
    /// Timestamp
    pub timestamp: Timestamp,
    
    /// Additional context
    pub context: HashMap<String, serde_json::Value>,
}

/// Log levels
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum LogLevel {
    Trace,
    Debug,
    Info,
    Warn,
    Error,
}

impl TestSummary {
    /// Create a new test summary
    pub fn new() -> Self {
        Self {
            success: true,
            stat: TestStat::new(),
            time: TestTime::new(),
            platform: Platform::new(),
            details: Vec::new(),
            root_dir: None,
        }
    }
    
    /// Add a test case summary
    pub fn add_case_summary(&mut self, case_summary: TestCaseSummary) {
        self.success = self.success && case_summary.success;
        self.stat.test_cases.total += 1;
        self.stat.test_steps.total += case_summary.stat.total;
        
        if case_summary.success {
            self.stat.test_cases.success += 1;
        } else {
            self.stat.test_cases.fail += 1;
        }
        
        self.stat.test_steps.successes += case_summary.stat.successes;
        self.stat.test_steps.failures += case_summary.stat.failures;
        
        // Merge action statistics
        for (action, count) in &case_summary.stat.actions {
            *self.stat.test_steps.actions.entry(action.clone()).or_insert(0) += count;
        }
        
        self.details.push(case_summary);
    }
    
    /// Finalize the summary (calculate duration, etc.)
    pub fn finalize(&mut self) {
        self.time.duration = (now() - self.time.start_at).num_milliseconds() as f64 / 1000.0;
    }
}

impl TestCaseSummary {
    /// Create a new test case summary
    pub fn new(id: Uuid, name: String, root_dir: String) -> Self {
        Self {
            id,
            name,
            success: true,
            stat: TestCaseStat::new(),
            time: TestTime::new(),
            in_out: InOutInfo::new(),
            logs: Vec::new(),
            step_results: Vec::new(),
            root_dir,
        }
    }
    
    /// Add a step result
    pub fn add_step_result(&mut self, step_result: StepResult) {
        self.success = self.success && step_result.success;
        self.stat.total += 1;
        
        if step_result.success {
            self.stat.successes += 1;
        } else {
            self.stat.failures += 1;
        }
        
        // Count actions
        for action in &step_result.actions {
            *self.stat.actions.entry(action.action_type.clone()).or_insert(0) += 1;
        }
        
        self.step_results.push(step_result);
    }
    
    /// Add a log entry
    pub fn add_log(&mut self, level: LogLevel, message: String, context: HashMap<String, serde_json::Value>) {
        self.logs.push(LogEntry {
            level,
            message,
            timestamp: now(),
            context,
        });
    }
    
    /// Finalize the test case summary
    pub fn finalize(&mut self) {
        self.time.duration = (now() - self.time.start_at).num_milliseconds() as f64 / 1000.0;
    }
}

impl TestStat {
    fn new() -> Self {
        Self {
            test_cases: TestCaseStats::new(),
            test_steps: TestStepStats::new(),
        }
    }
}

impl TestCaseStats {
    fn new() -> Self {
        Self {
            total: 0,
            success: 0,
            fail: 0,
        }
    }
}

impl TestStepStats {
    fn new() -> Self {
        Self {
            total: 0,
            successes: 0,
            failures: 0,
            actions: HashMap::new(),
        }
    }
}

impl TestCaseStat {
    fn new() -> Self {
        Self {
            total: 0,
            successes: 0,
            failures: 0,
            actions: HashMap::new(),
        }
    }
}

impl TestTime {
    fn new() -> Self {
        Self {
            start_at: now(),
            duration: 0.0,
        }
    }
}

impl Platform {
    fn new() -> Self {
        Self {
            platform_name: "AI Test Platform".to_string(),
            ai_test_platform_version: crate::VERSION.to_string(),
            rust_version: env!("RUSTC_VERSION").to_string(),
            platform: format!("{}-{}", std::env::consts::OS, std::env::consts::ARCH),
        }
    }
}

impl InOutInfo {
    fn new() -> Self {
        Self {
            config_vars: HashMap::new(),
            export_vars: HashMap::new(),
        }
    }
}

impl Default for TestSummary {
    fn default() -> Self {
        Self::new()
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::models::step::{StepType, StepResult};

    #[test]
    fn test_test_summary_creation() {
        let summary = TestSummary::new();
        assert!(summary.success);
        assert_eq!(summary.stat.test_cases.total, 0);
        assert_eq!(summary.details.len(), 0);
    }

    #[test]
    fn test_test_case_summary() {
        let id = Uuid::new_v4();
        let mut case_summary = TestCaseSummary::new(
            id,
            "test_case".to_string(),
            "/tmp".to_string(),
        );
        
        let step_result = StepResult::new(
            Uuid::new_v4(),
            "test_step".to_string(),
            StepType::Request,
        ).success();
        
        case_summary.add_step_result(step_result);
        
        assert_eq!(case_summary.stat.total, 1);
        assert_eq!(case_summary.stat.successes, 1);
        assert_eq!(case_summary.stat.failures, 0);
        assert!(case_summary.success);
    }

    #[test]
    fn test_summary_aggregation() {
        let mut summary = TestSummary::new();
        
        let case_summary = TestCaseSummary::new(
            Uuid::new_v4(),
            "test_case".to_string(),
            "/tmp".to_string(),
        );
        
        summary.add_case_summary(case_summary);
        
        assert_eq!(summary.stat.test_cases.total, 1);
        assert_eq!(summary.stat.test_cases.success, 1);
        assert_eq!(summary.details.len(), 1);
    }
}
